Release 10.1A: OpenEdge Development:
Progress Dynamics Advanced Development
Putting the value check into the window code
Now that the example is complete, you can look at an alternative way to do the same thing. Consider the procedures that you have just created. Most of the work is done in a custom super procedure attached to the read-only
Customerviewer on Page 1 of this window. It is important to keep in mind that you can define a custom super procedure for an object only at the master level, for the object itself, and not for a single instance of the object in a particular window. In this case, it might not be appropriate to have thedataAvailablecode associated with that viewer, because the special behavior it defines is really specific to this one window. In fact, the code would generate numerous errors if the viewer were in a different window, because the code as it stands assumes various things about what objects are in the window and what links there are between them. This in itself is worth correcting by improving the code to deal gracefully with its context if the window design is changed in some way.If you do not want to associate the behavior specifically with the
custcommentsvviewer, you can instead tie it to the window by putting the code into theoemaintwinsuper.pprocedure. Let’s look at that alternative and see what changes you have to make to the code in its new location.First, look at the
initializeObjectprocedure in oemaintwinsuper.p. The reason it seemed more natural to add the new behavior to theCustomerviewer is that it required localizing thedataAvailableevent, which the viewer subscribes to in the SDO. The container window does not subscribe to this event, and therefore does not normally receive it. This is the first thing you must change in order to move the code to the window.You can intercept this event in the window simply by adding a statement to subscribe to it. So this new version of the
Note: TheinitializeObjectcode has a few additional lines in it. The first line gets the handle of theCustomerSDO by retrieving theNavigation-Targetof the toolbar. TheNavigationTargetproperty is of data typeCHARACTER, so you must retrieve it into aCHARACTERvariable and then convert that to a handle.DYNAMIC-FUNCTIONoperation that takes place inside the{get}is forgiving enough that it actually casts theCHARACTERoutput from thegetNavigationTargetfunction directly into theHANDLEvariablehSDO. You would be well advised not to take advantage of the run-time engine’s generosity in this regard. In particular, if the{get}is turned into a direct lookup in the properties temp-table instead of a function call, you will get an error from your attempt to combine the two steps.Keep in mind that the reason the
NavigationTargetproperty is of typeCHARACTERis that there could be more than one navigation target for the toolbar, in which case the multiple targets would be represented as a list. In this case there is only one, because at the timeinitializeObjectgets run, only Page 0 and Page 1 of the folder have been created, so there is only one SDO in the window. Try not to make this kind of assumption in your own finished application code, so as to avoid errors if the window you are working with changes later on.The code you must add in this
initializeObjectprocedure is highlighted in bold, as shown:
Once you have the handle of the SDO you can subscribe to
dataAvailable. Again, remember to subscribe theTARGET-PROCEDURE, not the super procedure itself. Now the window will getdataAvailableevents just as the viewer does.The next step is to move the
dataAvailablecode itself from the viewer’s super procedure to the window’s. You can then delete the viewer’s super procedure and remove its association from the viewer in the Repository Maintenance tool.Because the
dataAvailablecode is now executed from the window’s perspective and not from the viewer’s, there are a few changes you must make. Let’s take a look at those.Identifying an object among all the contained objects
First, just as with the code in
Note: If you neglect to do this, you will get the error message, “Field Value too large for Integer,” because the run-time engine tries to convert some string such asinitializeObject, this version ofdataAvailablemust locate theCustomerSDO so that it can get the value of theCountryfield. And in this case, the warning about allowing for multipleNavigation-Targets becomes a reality. By the time this is executed, the other pages in the folder have likely been enabled, and the toolbar now has multipleNavigation-Targets. So you have no choice but to retrieve that value as a list and search through it for theCustomerSDO.12345,24356,45675into a handle, which is treated internally as a kind of integer.You could do this by checking the
LogicalObjectNameproperty and comparing that tocustcommentsv. This seems a little too restrictive, though, since it means that you must edit the code if you ever change the name of the viewer on Page 1. Since the code is checking the value of a field in theCustomertable, it seems safer to check for the presence of theCustomertable in the SDO, which is in itsTablesproperty.The code allows for the possibility that the
Customertable might be joined to some other table in the SDO, in which case theTablesproperty would be a list, as shown:
Scoping variables in super procedures
Here it is worth making another digression to discuss an important point. You might have realized that once
initializeObjecthas determined the SDO handle, it could just stash it in a variable defined in the Definitions section of the super procedure, and then this code would not have to locate it again. This is true, but it is also very dangerous. In the context of the example, this would work fine, because the super procedure is serving only theoemaintwinwindow, so the value of the SDO handle would be valid for the life of the window. But what if there were some circumstance under which the user could run two copies of the window at the same time? This might easily be allowed in the application, and they would both share a single running instance of the super procedure. The value of the SDO handle saved away by one running instance of the window would not be valid for the other running instance. The kinds of errors that this results in can be very insidious and difficult to track down. Or look at another possibility. What if you realize later that another window requires similar support for disabling actions based on a value check? If you are doing your job right, you will not create a new procedure to handle that and just copy code from the first one. You will start with the existing procedure, take the elements that are subject to change (such as the name of the field and its value) and turn them into parameters or properties of the object, and let the single procedure work for all windows requiring this type of support.Now it is very likely indeed that two different windows of this kind might be running at the same time, in which case they would be sharing a single instance of the super procedure, since much of the purpose of super procedures is to reduce memory use as well as r-code. The moral is that it is very bad form to hold any values across calls to different methods in a super procedure unless you are positive that a single instance of the super procedure will never be used to support two different objects at the same time.
Redirecting the PUBLISH statement from the window
The next step remains the same as before: Get the
Countryvalue and reset thecTargetsvariable to hold a list of theTableIO-Targets, as shown:
Look at the next statement:
When this code was in the viewer’s super procedure, the
TARGET-PROCEDUREwas the viewer itself. But now the code is executing from the window. Rather than going to the trouble of tracking down the handle of thecustcommentsvviewer, it is simpler just toRUNresetTableIOdirectly in the toolbar, whose handle you do have, since this has exactly the same effect. Remember that aPUBLISHstatement is basically the same as aRUNstatement except that the procedure handle the event gets run in is not specified in thePUBLISHstatement. So you should change the statement to this:
Reasons not to RUN SUPER from the window
The final change is also important to think about for a moment. Ordinarily, you must remember to include the
RUNSUPERstatement in your local procedure. Here you must remember to leave it out! The reason is that the window is not a native subscriber to thedataAvailableevent at all. It just needs to intercept it to take some action on behalf of other objects. For this reason, no super procedure of the window implementsdataAvailable, and if you try to invoke it with aRUNSUPERstatement, you will get an error at run time, much as if you had put in a statement that saidRUNdataAvailableIN<non-existent-handle>.So, you must remove the
RUNSUPERstatement as you convert the code to work in the window’s super procedure, as shown:
In some cases, especially where code is designed to work in a variety of situations, you might be faced with a case where there might or might not be a super procedure with the event in it. In this case it is perfectly OK to write
RUN SUPERNO-ERROR.Reasons not to use enableActions and disableActions
You might notice that there are two functions defined for toolbars in the
Panelclass calledenableActionsanddisableActions. You might be tempted to run those functions directly rather than setting theDisabledActionsproperty the example in this section described. This is generally not a good idea, as these functions are intended for internal use only.In particular, they are very short-term in their effect. That is, if you use
disableActionsto disable, say, the Delete action, it will have an immediate effect, but as soon as any operation occurs that resets the toolbar, such as a page change, the action will be enabled again. This is probably not what you want, and this is why it is better to use theDisabledActionsproperty to change the settings until you need to change them again.
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |